/**
 * Copyright (c) Software Institute of PKU
 * All rights reserved. 
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 */
package edu.pku.sei.modeler.contextmenu;

import java.util.List;

import org.eclipse.gef.EditDomain;
import org.eclipse.jface.action.Action;

import edu.pku.sei.modeler.commands.CreateLinkCommand;
import edu.pku.sei.modeler.model.common.ModelRegistry;
import edu.pku.sei.modeler.model.core.MetaClass;
import edu.pku.sei.modeler.model.core.MetaGeneralization;
import edu.pku.sei.modeler.model.core.MetaModelElement;
import edu.pku.sei.modeler.model.core.MetaReference;
import edu.pku.sei.modeler.model.structure.MetaDiagram;
import edu.pku.sei.modeler.model.structure.MetaLink;
import edu.pku.sei.modeler.model.structure.MetaNode;
import edu.pku.sei.modeler.model.structure.StructureFactory;

/**
 * @author zl
 *
 */
public class RebuildRelationAction extends Action {
	public static final String ID = 
		"edu.pku.modeler.ui.editor.contextmenu.RebuildRelationAction";
	private MetaNode host;
	private EditDomain domain;
	private StructureFactory factory;
	
	public RebuildRelationAction(EditDomain domain, MetaNode host) {
		super("Rebuild relations");
		this.setId(ID);
		this.domain = domain;
		this.host = host;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.gef.ui.actions.WorkbenchPartAction#calculateEnabled()
	 */
	protected boolean calculateEnabled() {
		if(host == null)return false;
		if(domain == null)return false;
		if(!host.isReferenceShape())return false;
		try {
			factory = (StructureFactory) ModelRegistry.getRegistry().getStructureFactory(host.getDiagram().getModel());
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/* (non-Javadoc)
     * Method declared on IAction.
     */
    public boolean isEnabled() {
        return calculateEnabled();
    }
    
    public void run() {
    	if(host.getModelElement() instanceof MetaClass) {
			checkAssociations((MetaClass)host.getModelElement());
			checkGeneralizations((MetaClass)host.getModelElement());
		}
	}
    
    /**
     * check outgoing and incoming associations
     * @param cls
     */
    protected void checkAssociations(MetaClass cls) {
		List refs = cls.getReferences();
		MetaDiagram diagram = host.getDiagram();
		for(int i=0; i<refs.size(); i++) {
			MetaReference ref = (MetaReference)refs.get(i);
			if(hasLink(ref))continue;
			MetaNode target = findNodeOnDiagram(ref.getType(), diagram);
			if(target != null) {
				MetaLink link = factory.createMetaLink();
				link.setReferenceShape(true);
				link.setModelElement(ref);
				CreateLinkCommand cmd = new CreateLinkCommand(link, host);
				cmd.setTarget(target);
				domain.getCommandStack().execute(cmd);
			}
		}
		List nodes = host.getDiagram().getNodes();
		for(int i=0; i<nodes.size(); i++) {
			MetaNode node = (MetaNode)nodes.get(i);
			if(!(node.getModelElement() instanceof MetaClass)) continue;
			refs = ((MetaClass)node.getModelElement()).getReferences();
			for(int j=0; j<refs.size(); j++) {
				MetaReference ref = (MetaReference)refs.get(j);
				if(ref.getType()!=cls)continue;
				if(hasLink(ref))continue;
				MetaLink link = factory.createMetaLink();
				link.setReferenceShape(true);
				link.setModelElement(ref);
				CreateLinkCommand cmd = new CreateLinkCommand(link, node);
				cmd.setTarget(host);
				domain.getCommandStack().execute(cmd);
			}
		}
	}
	
	protected void checkGeneralizations(MetaClass cls) {
		List gens = cls.getGenerals();
		MetaDiagram diagram = host.getDiagram();
		for(int i=0; i<gens.size(); i++) {
			MetaGeneralization gen = (MetaGeneralization)gens.get(i);
			if(hasLink(gen))continue;
			MetaNode target = findNodeOnDiagram(gen.getGeneral(), diagram);
			if(target != null) {
				MetaLink link = factory.createMetaLink();
				link.setReferenceShape(true);
				link.setModelElement(gen);
				CreateLinkCommand cmd = new CreateLinkCommand(link, host);
				cmd.setTarget(target);
				domain.getCommandStack().execute(cmd);
			}
		}
		List nodes = host.getDiagram().getNodes();
		for(int i=0; i<nodes.size(); i++) {
			MetaNode node = (MetaNode)nodes.get(i);
			if(!(node.getModelElement() instanceof MetaClass)) continue;
			gens = ((MetaClass)node.getModelElement()).getGenerals();
			for(int j=0; j<gens.size(); j++) {
				MetaGeneralization gen = (MetaGeneralization)gens.get(j);
				if(gen.getGeneral()!=cls)continue;
				if(hasLink(gen))continue;
				MetaLink link = factory.createMetaLink();
				link.setReferenceShape(true);
				link.setModelElement(gen);
				CreateLinkCommand cmd = new CreateLinkCommand(link, node);
				cmd.setTarget(host);
				domain.getCommandStack().execute(cmd);
			}
		}
	}
	
	protected boolean hasLink(MetaModelElement element) {
		List<MetaLink> outgoings = host.getOutgoings();
		for(MetaLink l:outgoings) {
			if(l.getModelElement() == element)return true;
		}
		List<MetaLink> incomings = host.getIncomings();
		for(MetaLink l:incomings) {
			if(l.getModelElement() == element)return true;
		}
		return false;
	}
	/**
	 * 
	 * @param element
	 * @param diagram
	 * @return
	 */
	protected MetaNode findNodeOnDiagram(MetaModelElement element, MetaDiagram diagram) {
		List<MetaNode>nodes = diagram.getNodes();
		for(MetaNode n:nodes) {
			if(n.getModelElement() == element)return n;
		}
		return null;
	}
}
